package org.loy.eclipse.post_debug_perspective.preferences;

import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.preference.*;
import org.eclipse.ui.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;

import org.loy.eclipse.post_debug_perspective.Activator;

// TODO make cleanup
// TODO document this module

public class PerspectiveAssociationsPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage
{
	// Constants
	private static String PREFERENCE_DELIMITER = "\n";

	//
	public PerspectiveAssociationsPreferencePage()
	{
		super(GRID);
		setPreferenceStore(Activator.getDefault().getPreferenceStore());
		setDescription(""); // TODO decide, maybe add some description here
	}

	public static Map<String, String> getPerspectiveAssociations()
	{
		return unpackPerspectiveAssociationsPreference(
			Activator.getDefault().getPreferenceStore().getString(PreferenceConstants.P_PERSPECTIVE_ASSOCIATIONS) );
	}

	@Override
	public void init(IWorkbench workbench)
	{
		this.workbench = workbench;
		selectedPerspectiveAssociations = unpackPerspectiveAssociationsPreference(
			getPreferenceStore().getString(PreferenceConstants.P_PERSPECTIVE_ASSOCIATIONS) );
	}
	
	@Override
	public void createFieldEditors()
	{
	}
	
	public boolean performOk() {
		getPreferenceStore().setValue(
			PreferenceConstants.P_PERSPECTIVE_ASSOCIATIONS,
			packPerspectiveAssociationsPreference(selectedPerspectiveAssociations)
		);
		return super.performOk();
	}
	
	protected void performDefaults()
	{
		// TODO implement
		super.performDefaults();
	}
	
	@Override
	protected Control createContents(Composite parent)
	{
		// Init application types(ILaunchConfigurationType) list --------------
		lauchConfigurationTypesList = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
		
		lauchConfigurationTypesList.setHeaderVisible(false);
		lauchConfigurationTypesList.setLinesVisible(false);			
		
		TableColumn column = new TableColumn(lauchConfigurationTypesList, SWT.NONE);
		// TODO fix ordering issue
		lauchConfigurationTypesList.setSortColumn(column);
		lauchConfigurationTypesList.setSortDirection(SWT.DOWN);

		
// TODO -----------------------------------------------------------------------
//		table.setLayout(new GridLayout(2, false));

//		GridData gridData = new GridData(GridData.FILL_VERTICAL);
//		gridData.widthHint = 220;
//		gridData.heightHint = 250;
//		table.setLayout(layout)  setLayout(gridData);
//-----------------------------------------------------------------------------
		
		// Extract content
		ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
		Map<TableItem, ILaunchConfigurationType> launchConfigurationTypesListItems
			= new HashMap<TableItem, ILaunchConfigurationType>();

		for (ILaunchConfigurationType launchConfigurationType : launchManager.getLaunchConfigurationTypes()) {

			// Skip configuration types that cannot debug
			if (!launchConfigurationType.supportsMode("debug")) {
				continue ;
			}

			// Skip non-public(not allowed to be launched by user) perspectives
			if (!launchConfigurationType.isPublic()) {
				continue ;
			}
			
			// Add table cell data
			TableItem item = new TableItem(lauchConfigurationTypesList, SWT.NONE);
			item.setText( new String[] { launchConfigurationType.getName() } );
			item.setImage(DebugUITools.getImage(launchConfigurationType.getIdentifier()));

			// Save TableItem - ILaunchConfigurationType association
			launchConfigurationTypesListItems.put(item, launchConfigurationType);
		}
		
		column.pack();

		// Create perspective selection combobox ------------------------------
		IPerspectiveDescriptor[] perspectives = workbench.getPerspectiveRegistry().getPerspectives();
		
		// Make list of combobox choices
		String[] comboboxEntryNames = new String[perspectives.length + 1]; // TODO change to ArryList

		comboboxEntryNames[0] = "None";
		for(int i = 0; i < perspectives.length; i++) {
			comboboxEntryNames[i + 1] = perspectives[i].getLabel();
		}

		// Store perspective ids, associated with combobox entry names
		String[] perspectiveIds = new String[perspectives.length + 1];

		perspectiveIds[0] = PreferenceConstants.DO_NOT_SWITCH_PERSPECTIVE;
		for(int i = 0; i < perspectives.length; i++) {
			perspectiveIds[i + 1] = perspectives[i].getId();
		}

		// Init combobox itself
		perspectiveCombobox = new Combo(parent, SWT.READ_ONLY);
		perspectiveCombobox.setItems(comboboxEntryNames);
		
		// Set selection listeners --------------------------------------------
		LauchConfigurationTypesListSelectionListener lauchConfigurationTypesListSelectionListener
			= new LauchConfigurationTypesListSelectionListener(
				perspectiveCombobox,
				selectedPerspectiveAssociations,
				launchConfigurationTypesListItems,
				perspectiveIds
			);
		
		lauchConfigurationTypesList.addSelectionListener(lauchConfigurationTypesListSelectionListener);
		
		perspectiveCombobox.addSelectionListener(
			new PerspectiveComboboxSelectionListener(
				perspectiveCombobox,
				lauchConfigurationTypesList,
				selectedPerspectiveAssociations,
				launchConfigurationTypesListItems,
				perspectiveIds
			)
		);
		
		// select first item --------------------------------------------------
		lauchConfigurationTypesList.select(0);
		TableItem selectedLaunchConfigurationTableItem = lauchConfigurationTypesList.getSelection()[0];
		String selectedLaunchConfigurationTypeId
			= launchConfigurationTypesListItems.get(selectedLaunchConfigurationTableItem).getIdentifier();
		
		lauchConfigurationTypesListSelectionListener.updateComboboxSelection(selectedLaunchConfigurationTypeId);

		return parent;
	}	
	
	private static Map<String, String> unpackPerspectiveAssociationsPreference(String packedString)
	{
		Map<String, String> associations = new HashMap<String, String>();
		
		// TODO process errors here(in case of malformed packedString)
		StringTokenizer tokenizer = new StringTokenizer(packedString, PREFERENCE_DELIMITER);
		for (int i = 0; i < tokenizer.countTokens(); i++) {
			String launchConfigurationTypeId = tokenizer.nextToken();
			String perspectiveId = tokenizer.nextToken();
			
			associations.put(launchConfigurationTypeId, perspectiveId);
		}

		return associations;
	}
	
	
	private static String packPerspectiveAssociationsPreference(Map<String, String> associations)
	{
		String packedString = "";
		for (String launchConfigurationTypeId : associations.keySet()) {
			packedString += launchConfigurationTypeId + PREFERENCE_DELIMITER
				+ associations.get(launchConfigurationTypeId) + PREFERENCE_DELIMITER;
		}
		return packedString;
	}
	
	private Map<String, String> selectedPerspectiveAssociations;
	private IWorkbench workbench;

	// Widgets
	private Table lauchConfigurationTypesList;
	private Combo perspectiveCombobox;
}

class LauchConfigurationTypesListSelectionListener implements SelectionListener
{
	public LauchConfigurationTypesListSelectionListener(
		Combo perspectiveCombobox,
		Map<String, String> selectedPerspectiveAssociations,
		Map<TableItem, ILaunchConfigurationType> launchConfigurationTypesListItems,
		String[] perspectiveIds
	)
	{
		this.perspectiveCombobox = perspectiveCombobox;
		this.selectedPerspectiveAssociations = selectedPerspectiveAssociations;
		this.launchConfigurationTypesListItems = launchConfigurationTypesListItems;
		this.perspectiveIds = perspectiveIds;
	}
	
	// TODO add error chacks here
	@Override
	public void widgetSelected(SelectionEvent e)
	{
		String selectedLaunchConfigurationTypeId = launchConfigurationTypesListItems.get(e.item).getIdentifier();
		updateComboboxSelection(selectedLaunchConfigurationTypeId);
	}
	
	@Override
	public void widgetDefaultSelected(SelectionEvent e)
	{
		widgetSelected(e);
	}
	
	public void updateComboboxSelection(String selectedLaunchConfigurationTypeId)
	{
		if (!selectedPerspectiveAssociations.containsKey(selectedLaunchConfigurationTypeId)) {
			perspectiveCombobox.select(0);
		} else {
			String perspectiveId = selectedPerspectiveAssociations.get(selectedLaunchConfigurationTypeId);

			int foundPerspectiveIndex = 0;
			for (int i = 0; i < perspectiveIds.length; i++) {
				if (perspectiveIds[i].equals(perspectiveId)) {
					foundPerspectiveIndex = i;
					break ;
				}
			}
			
			perspectiveCombobox.select(foundPerspectiveIndex);
		}
	}
	
	Combo perspectiveCombobox;
	Map<TableItem, ILaunchConfigurationType> launchConfigurationTypesListItems;
	Map<String, String> selectedPerspectiveAssociations;
	String[] perspectiveIds;
}


class PerspectiveComboboxSelectionListener implements SelectionListener
{
	public PerspectiveComboboxSelectionListener(
		Combo perspectiveCombobox,
		Table lauchConfigurationTypesList,
		Map<String, String> selectedPerspectiveAssociations,
		Map<TableItem, ILaunchConfigurationType> launchConfigurationTypesListItems,
		String[] perspectiveIds
	)
	{
		this.perspectiveCombobox = perspectiveCombobox;
		this.lauchConfigurationTypesList = lauchConfigurationTypesList;
		this.selectedPerspectiveAssociations = selectedPerspectiveAssociations;
		this.launchConfigurationTypesListItems = launchConfigurationTypesListItems;
		this.perspectiveIds = perspectiveIds;
	}
	
	// TODO add error checks here
	@Override
	public void widgetSelected(SelectionEvent e)
	{
		int selectedPerspectiveIdIndex = ((Combo)e.widget).getSelectionIndex();
		String selectedlauchConfigurationTypeId = 
			launchConfigurationTypesListItems.get(lauchConfigurationTypesList.getSelection()[0]).getIdentifier();
		
		if (selectedPerspectiveIdIndex != 0) {
			selectedPerspectiveAssociations.put(
				selectedlauchConfigurationTypeId, perspectiveIds[selectedPerspectiveIdIndex] );
		} else {
			selectedPerspectiveAssociations.remove(selectedlauchConfigurationTypeId);
		}
	}
	
	@Override
	public void widgetDefaultSelected(SelectionEvent e)
	{
		widgetSelected(e);
	}
	
	Combo perspectiveCombobox;
	Table lauchConfigurationTypesList;
	Map<TableItem, ILaunchConfigurationType> launchConfigurationTypesListItems;
	Map<String, String> selectedPerspectiveAssociations;
	String[] perspectiveIds;
}
